package skiree.host.abetsy.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import skiree.host.abetsy.api.dao.ConfigDao;
import skiree.host.abetsy.dao.LocalAnimeMapper;
import skiree.host.abetsy.dao.LocalEpisodeMapper;
import skiree.host.abetsy.model.LocalAnime;
import skiree.host.abetsy.model.LocalComment;
import skiree.host.abetsy.model.LocalConfig;
import skiree.host.abetsy.model.LocalEpisode;
import skiree.host.abetsy.model.ddp.*;
import skiree.host.abetsy.service.CoreService;
import skiree.host.abetsy.service.LocalService;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.CompletableFuture;

@Component
public class CoreServiceImpl implements CoreService {

    @Value("${base.url}")
    private String baseURL;

    @Resource
    private LocalService localService;

    @Resource
    private LocalEpisodeMapper localEpisodeMapper;

    @Resource
    private LocalAnimeMapper localAnimeMapper;

    @Resource
    private ConfigDao configDao;

    public boolean expandNetResources() {
        QueryWrapper<LocalConfig> configQuery = new QueryWrapper<>();
        configQuery.eq("name", "expand");
        LocalConfig localConfig = configDao.selectOne(configQuery);
        return localConfig == null || Boolean.parseBoolean(localConfig.getValue());
    }

    @Override
    public DdpSearchEpisodes<Object> search(String anime, String episode) {
        DdpSearchEpisodes<Object> search = new DdpSearchEpisodes<>();
        TimeInterval interval = DateUtil.timer();
        if (expandNetResources()) {
            // 同时执行两个方法
            CompletableFuture<List<SearchEpisodes>> futureA = CompletableFuture.supplyAsync(() -> searchByOfficial(anime, episode));
            CompletableFuture<List<SearchEpisodes>> futureB = CompletableFuture.supplyAsync(() -> localService.searchByLocal(anime, episode));
            CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureA, futureB);
            try {
                anyOf.get();
            } catch (Exception ignore) {
            }
            if (futureA.isDone()) {
                List<SearchEpisodes> tempA = futureA.join();
                if (!tempA.isEmpty()) {
                    search.setAnimes(tempA);
                } else {
                    List<SearchEpisodes> tempB = futureB.join();
                    if (!tempB.isEmpty()) {
                        search.setAnimes(tempB);
                    } else {
                        // A B 均为空需要考虑加载此剧集
                        localService.handleUnknownAnime(anime, episode);
                        search.setAnimes(tempB);
                    }
                }
            } else {
                List<SearchEpisodes> tempB = futureB.join();
                if (!tempB.isEmpty()) {
                    search.setAnimes(tempB);
                } else {
                    search.setAnimes(futureA.join());
                }
            }
        } else {
            search.setAnimes(localService.searchByLocal(anime, episode));
        }
        search.setTime(interval.interval());
        return search;
    }

    @Override
    public DdpComment comment(String episodeId, String chConvert, String withRelated) {
        DdpComment comment = new DdpComment();
        TimeInterval interval = DateUtil.timer();
        List<LocalComment> comments;
        if (episodeId.startsWith("-")) {
            comments = localService.commentByLocal(episodeId);
        } else {
            LocalEpisode localEpisode = localEpisodeMapper.selectById(Integer.parseInt(episodeId));
            if (localEpisode != null) {
                comments = localService.commentByLocal(episodeId);
            } else {
                comments = commentByOfficial(episodeId, chConvert, withRelated);
            }
        }
        comment.setCount(comments.size());
        comment.setComments(comments);
        comment.setTime(interval.interval());
        return comment;
    }

    @Override
    public DdpSearchEpisodes<Object> onSearch(String anime) {
        DdpSearchEpisodes<Object> search = new DdpSearchEpisodes<>();
        TimeInterval interval = DateUtil.timer();
        // 同时执行两个方法
        CompletableFuture<List<SearchEpisodes>> futureA = CompletableFuture.supplyAsync(() -> onSearchByOfficial(anime));
        CompletableFuture<List<SearchEpisodes>> futureB = CompletableFuture.supplyAsync(() -> localService.onSearchByLocal(anime));
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureA, futureB);
        try {
            anyOf.get();
        } catch (Exception ignore) {
        }
        if (futureA.isDone()) {
            List<SearchEpisodes> tempA = futureA.join();
            if (!tempA.isEmpty()) {
                search.setAnimes(tempA);
            } else {
                search.setAnimes(futureB.join());
            }
        } else {
            List<SearchEpisodes> tempB = futureB.join();
            if (!tempB.isEmpty()) {
                search.setAnimes(tempB);
            } else {
                search.setAnimes(futureA.join());
            }
        }
//        List<Anime> animeList = onSearchByOfficial(anime);
//        if (animeList.isEmpty()) {
//            animeList = localService.onSearchByLocal(anime);
//        }
//        search.setAnimes(animeList);
        search.setTime(interval.interval());
        return search;
    }

    @Override
    public DdpSearchAnime<Object> searchAnime(String keyword, String type) {
        DdpSearchAnime<Object> search = new DdpSearchAnime<>();
        TimeInterval interval = DateUtil.timer();
        if (expandNetResources()){
            // 同时执行两个方法
            CompletableFuture<List<SearchAnime>> futureA = CompletableFuture.supplyAsync(() -> searchAnimeByOfficial(keyword, type));
            CompletableFuture<List<SearchAnime>> futureB = CompletableFuture.supplyAsync(() -> localService.searchAnimeByLocal(keyword));
            CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureA, futureB);
            try {
                anyOf.get();
            } catch (Exception ignore) {
            }
            if (futureA.isDone()) {
                List<SearchAnime> tempA = futureA.join();
                if (!tempA.isEmpty()) {
                    search.setAnimes(tempA);
                } else {
                    search.setAnimes(futureB.join());
                }
            } else {
                List<SearchAnime> tempB = futureB.join();
                if (!tempB.isEmpty()) {
                    search.setAnimes(tempB);
                } else {
                    search.setAnimes(futureA.join());
                }
            }
        }else {
            search.setAnimes(localService.searchAnimeByLocal(keyword));
        }
        search.setTime(interval.interval());
        return search;
    }

    @Override
    public DdpBanGuMi<Object> banGuMi(String animeId) {
        DdpBanGuMi<Object> bangumi = new DdpBanGuMi<>();
        TimeInterval interval = DateUtil.timer();
        // 先判断本地是否存在此ID
        QueryWrapper<LocalAnime> animeQueryWrapper = new QueryWrapper<>();
        animeQueryWrapper.eq("id", Integer.parseInt(animeId));
        LocalAnime selectOne = localAnimeMapper.selectOne(animeQueryWrapper);
        if (selectOne == null) {
            bangumi.setBangumi(onBanGuMiByOfficial(animeId));
        } else {
            String nameMk = selectOne.getTitle() + " 第" + Convert.numberToChinese(selectOne.getSeason(), false) + "季";
            Map<String, Object> data = new HashMap<>();
            // 设置其他数据
            data.put("metadata", Collections.singletonList("中文名: " + nameMk));
            data.put("animeId", selectOne.getId());
            data.put("rating", 5.0);
            data.put("favoriteStatus", null);
            data.put("type", "web");
            data.put("bangumiId", animeId);
            data.put("animeTitle", nameMk);
            data.put("searchKeyword", nameMk);
            data.put("bangumiUrl", "https://bangumi.tv/subject/291635");
            data.put("isOnAir", false);
            data.put("imageUrl", "https://img.dandanplay.net/anime/16720_medium.jpg");
            data.put("relateds", new ArrayList<>());
            // 设置子集
            QueryWrapper<LocalEpisode> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("anime_id", selectOne.getId());
            queryWrapper.eq("status", "loaded");
            List<LocalEpisode> localEpisodes = localEpisodeMapper.selectList(queryWrapper);
            List<Map<String, Object>> episodes = new ArrayList<>();
            for (LocalEpisode localEpisode : localEpisodes) {
                Map<String, Object> temp = new HashMap<>();
                temp.put("episodeTitle", localEpisode.getTitle());
                temp.put("lastWatched", null);
                temp.put("seasonId", null);
                temp.put("airDate", "2022-07-31T00:00:00");
                temp.put("episodeId", localEpisode.getId());
                temp.put("episodeNumber", localEpisode.getEpisodeIndex().toString());
                episodes.add(temp);
            }
            // 设置评分
            Map<String, Object> ratingDetails = new HashMap<>();
            ratingDetails.put("Bangumi评分", 5.0);
            ratingDetails.put("Anidb完结后评分", 5.0);
            ratingDetails.put("弹弹play连载中评分", 5.0);
            ratingDetails.put("弹弹play完结后评分", 5.0);
            ratingDetails.put("Anidb连载中评分", 5.0);
            ratingDetails.put("Anidb评论员评分", 5.0);
            ratingDetails.put("ratingDetails", ratingDetails);
            // 设置其他数据
            data.put("summary", "拓展弹幕中");
            data.put("seasons", new ArrayList<>());
            data.put("similars", new ArrayList<>());
            data.put("typeDescription", "网络放送");
            data.put("isFavorited", false);
            data.put("userRating", 0);
            data.put("airDay", 0);
            data.put("comment", null);
            data.put("isRestricted", false);
            // 在线数据
            data.put("onlineDatabases", new ArrayList<>());
            // 标题
            List<Map<String, Object>> titles = new ArrayList<>();
            Map<String, Object> tempTitles = new HashMap<>();
            tempTitles.put("language", "主标题");
            tempTitles.put("title", nameMk);
            titles.add(tempTitles);
            data.put("titles", titles);
            // 标记
            List<Map<String, Object>> tags = new ArrayList<>();
            Map<String, Object> tempTags = new HashMap<>();
            tempTags.put("name", "国产");
            tempTags.put("count", 101);
            tempTags.put("id", 1);
            tags.add(tempTags);
            data.put("tags", tags);
            // 设置数据
            data.put("episodes", episodes);
            bangumi.setBangumi(data);
        }
        bangumi.setTime(interval.interval());
        return bangumi;
    }

    private Object onBanGuMiByOfficial(String animeId) {
        try {
            String result = HttpUtil.get(baseURL + "/api/v2/bangumi/" + animeId, CharsetUtil.CHARSET_UTF_8);
            com.alibaba.fastjson.JSONObject jsonObject = com.alibaba.fastjson.JSONObject.parseObject(result);
            return jsonObject.get("bangumi");
        } catch (Exception ignore) {
        }
        return null;
    }

    private List<SearchAnime> searchAnimeByOfficial(String keyword, String type) {
        List<SearchAnime> animeList = new ArrayList<>();
        try {
            String result = HttpUtil.get(baseURL + "/api/v2/search/anime?keyword=" + URLEncoder.encode(keyword, "UTF-8") + "&type=" + URLEncoder.encode(type, "UTF-8"), CharsetUtil.CHARSET_UTF_8);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            List<SearchAnime> animeArray = jsonObject.getJSONArray("animes").toList(SearchAnime.class);
            animeList.addAll(animeArray);
        } catch (Exception ignore) {
        }
        return animeList;
    }

    private List<SearchEpisodes> onSearchByOfficial(String anime) {
        List<SearchEpisodes> searchEpisodesList = new ArrayList<>();
        try {
            String result = HttpUtil.get(baseURL + "/api/v2/search/episodes?anime=" + URLEncoder.encode(anime, "UTF-8"), CharsetUtil.CHARSET_UTF_8);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            List<SearchEpisodes> searchEpisodesArray = jsonObject.getJSONArray("animes").toList(SearchEpisodes.class);
            searchEpisodesList.addAll(searchEpisodesArray);
        } catch (Exception ignore) {
        }
        return searchEpisodesList;
    }

    private List<SearchEpisodes> searchByOfficial(String anime, String episode) {
        List<SearchEpisodes> searchEpisodesList = new ArrayList<>();
        try {
            String result = HttpUtil.get(baseURL + "/api/v2/search/episodes?anime=" + URLEncoder.encode(anime, "UTF-8") + "&episode=" + episode, CharsetUtil.CHARSET_UTF_8);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            List<SearchEpisodes> searchEpisodesArray = jsonObject.getJSONArray("animes").toList(SearchEpisodes.class);
            searchEpisodesList.addAll(searchEpisodesArray);
        } catch (Exception ignore) {
        }
        return searchEpisodesList;
    }

    private List<LocalComment> commentByOfficial(String episodeId, String chConvert, String withRelated) {
        List<LocalComment> comments = new ArrayList<>();
        try {
            String externalCommentUrl = baseURL + "/api/v2/comment/" + episodeId + "?chConvert=" + chConvert + "&withRelated=" + withRelated;
            Request request = new Request.Builder().url(externalCommentUrl).method("GET", null).build();
            Response response = new OkHttpClient().newBuilder().build().newCall(request).execute();
            String internalCommentUrl = response.request().url().toString();
            String result = HttpUtil.get(internalCommentUrl, CharsetUtil.CHARSET_UTF_8);
            JSONObject jsonObject = JSONUtil.parseObj(result);
            List<LocalComment> commentArray = jsonObject.getJSONArray("comments").toList(LocalComment.class);
            comments.addAll(commentArray);
        } catch (Exception ignore) {
        }
        return comments;
    }

}
